module CyclomaticComplexity

/**
 *
 * Module        : CC (Cyclometric Complexity)
 * Authors       : Ben Kwint & Arie van der Veek
 * Date          : November 13th 2011
 * Description   : Calculates the cyclometric complexity of a software system
 *                 written in Java. The calculator uses the JDT generator in
 *                 Eclipse to find all the java control flow statements
 *				   http://docs.oracle.com/javase/tutorial/java/nutsandbolts/flow.html
 *          
 *                 More info on CC can be found at:
 *                 http://en.wikipedia.org/wiki/Cyclometric_complexity
 *				   The counting method described in 
 *				   G.K. Gill and C.F. Kemerer. Cyclomatic complexity density and software maintenance
 *	 			   productivity. Software Engineering, IEEE Transactions on, 17(12):1284{1288, 1991.
 *				   is used
 *					
 *				   The two followin exceptions are made: 
 					1) The return statement in case it is NOT the final statement of a method is not 
 					counted, because it is relativly hard to code and is not present in the
 				   	source code under investigation.
 				   	2) Instead of counting the Swith statement and the total cases -2, as proposed
 				   	in the paper of Gill, only all the case statements are counted and not the Switch
 				   	statement. All cases are a path in the graph. The switch statement itself is not.
 				   	  
 */

import lang::java::jdt::Java;
import lang::java::jdt::JDT;
import lang::java::jdt::JavaADT;

import Graph;
import Set;
import List;
import IO;

// Get the cyclometric complexity of the function given in the AstNode tree
public int calculateCC(AstNode tree)
{
	int cc = 1;// basecount of CC is always one for one default control flow.
	// all cases that have to be taken into account for the cyclometric complexity
	visit(tree) {
		// Conditionals
		case ifStatement(_, _, _): cc += 1;
		case switchCase(bool isDefault, _): cc += 1;
		case conditionalExpression(_, _, _): cc += 1;
		case infixExpression(str operator, _, _, _):  {if ((operator == "||") || (operator == "&&")) cc += 1;}
		// Branching statements
		case breakStatement(_) :cc += 1;
	    case continueStatement(_) :cc += 1;
		// Iterative loops
		case whileStatement(_, _): cc += 1;
		case forStatement(_, _, _, _):cc += 1;
		case enhancedForStatement(_, _, _):cc += 1;
		case doStatement(_, _): cc += 1;
		// Exception handling statements, these also cause extra execution paths. Except the finally
		case throwStatement(_): cc += 1;
		case catchClause(_, _): cc += 1;
	};
	return cc;
}